<template>
  <div class="asset-profile-graph-page">
    <div class="page-header">
      <el-button @click="handleBack">返回</el-button>
      <h2 class="page-title">资产画像图谱</h2>
    </div>

    <div class="graph-content" v-loading="loading">
      <!-- 左侧：资产信息卡 + 关键信息条 -->
      <div class="left-panel">
        <!-- 资产基础信息 -->
        <el-card class="info-card" :body-style="{ padding: '15px' }">
          <h3 class="card-title">资产基础信息</h3>
          <el-descriptions :column="1" border size="small">
            <el-descriptions-item label="IP">
              {{ assetInfo.ip || "-" }}
            </el-descriptions-item>
            <el-descriptions-item label="端口">
              {{ assetInfo.port || "-" }}
            </el-descriptions-item>
            <el-descriptions-item label="服务">
              {{ assetInfo.service || "-" }}
            </el-descriptions-item>
            <el-descriptions-item label="任务">
              {{ assetInfo.task_name || "-" }}
            </el-descriptions-item>
            <el-descriptions-item label="当前归属部门">
              <el-tag
                v-if="assetInfo.current_department"
                type="success"
                size="small"
              >
                {{ assetInfo.current_department }}
              </el-tag>
              <span v-else class="no-dept">待确认</span>
            </el-descriptions-item>
          </el-descriptions>
        </el-card>

        <!-- 关键信息条（标签云） -->
        <el-card class="tags-card" :body-style="{ padding: '15px' }">
          <h3 class="card-title">关键信息</h3>
          <div class="tags-container">
            <el-tag
              v-if="assetInfo.is_high_risk"
              type="danger"
              size="small"
              style="margin: 4px"
            >
              高风险
            </el-tag>
            <el-tag
              v-if="assetInfo.is_internet_accessible"
              type="warning"
              size="small"
              style="margin: 4px"
            >
              互联网可达
            </el-tag>
            <el-tag
              v-for="(dept, index) in departmentCandidates.slice(0, 2)"
              :key="index"
              type="info"
              size="small"
              style="margin: 4px"
            >
              候选: {{ dept.department }}
            </el-tag>
            <el-tag
              v-for="(keyword, index) in keywords.slice(0, 3)"
              :key="index"
              type="success"
              size="small"
              style="margin: 4px"
            >
              命中关键词: {{ keyword.keyword }}
            </el-tag>
            <el-tag
              v-if="similarAssets.length > 0"
              type="info"
              size="small"
              style="margin: 4px"
            >
              相似资产: {{ similarAssets.length }} 个
            </el-tag>
          </div>
        </el-card>
      </div>

      <!-- 中间：图谱视图 -->
      <div class="graph-area">
        <div class="graph-toolbar">
          <div class="toolbar-left">
            <el-input
              v-model="searchKeyword"
              placeholder="搜索节点..."
              @keyup.enter="searchNodes"
              class="search-input"
              size="small"
            >
              <template #append>
                <el-button @click="searchNodes" size="small">搜索</el-button>
              </template>
            </el-input>
          </div>
          <div class="toolbar-right">
            <el-button size="small" @click="refreshGraph">刷新图谱</el-button>
            <el-button size="small" @click="exportGraphPNG">导出图谱</el-button>
          </div>
        </div>

        <div class="graph-container">
          <div ref="graphChartRef" class="echarts-graph"></div>
        </div>
      </div>

      <!-- 右侧：图例 + 关系明细列表 -->
      <div class="right-panel">
        <!-- 图例 -->
        <el-card class="legend-card" :body-style="{ padding: '15px' }">
          <h3 class="card-title">图例</h3>
          <ul class="legend-list">
            <li>
              <span class="legend-icon asset"></span>
              <span>资产节点</span>
            </li>
            <li>
              <span class="legend-icon department"></span>
              <span>部门/候选部门</span>
            </li>
            <li>
              <span class="legend-icon task"></span>
              <span>任务 / IP 网段</span>
            </li>
            <li>
              <span class="legend-icon keyword"></span>
              <span>关键词 / 相似资产</span>
            </li>
          </ul>
        </el-card>

        <!-- 关系明细列表 -->
        <el-card class="detail-card" :body-style="{ padding: '15px' }">
          <h3 class="card-title">关系明细</h3>

          <!-- 部门候选及置信度 -->
          <div class="detail-section">
            <h4>部门候选及置信度</h4>
            <el-table
              :data="departmentCandidates"
              size="small"
              border
              max-height="200"
            >
              <el-table-column
                prop="rank"
                label="排名"
                width="60"
                align="center"
              />
              <el-table-column prop="department" label="部门" min-width="150" />
              <el-table-column label="置信度" width="100" align="center">
                <template #default="{ row }">
                  {{ (row.confidence * 100).toFixed(0) }}%
                </template>
              </el-table-column>
            </el-table>
          </div>

          <!-- 命中关键词及权重 -->
          <div class="detail-section">
            <h4>命中关键词及权重</h4>
            <el-table :data="keywords" size="small" border max-height="200">
              <el-table-column prop="keyword" label="关键词" min-width="120" />
              <el-table-column label="权重" width="100" align="center">
                <template #default="{ row }">
                  {{ (row.weight * 100).toFixed(0) }}%
                </template>
              </el-table-column>
              <el-table-column
                prop="hit_field"
                label="命中字段"
                min-width="120"
              />
            </el-table>
          </div>

          <!-- 相似资产列表 -->
          <div class="detail-section">
            <h4>相似资产列表</h4>
            <el-table
              :data="similarAssets"
              size="small"
              border
              max-height="200"
            >
              <el-table-column prop="ip" label="IP" min-width="120" />
              <el-table-column
                prop="port"
                label="端口"
                width="80"
                align="center"
              />
              <el-table-column prop="service" label="服务" min-width="100" />
              <el-table-column label="相似度" width="100" align="center">
                <template #default="{ row }">
                  {{ (row.similarity * 100).toFixed(0) }}%
                </template>
              </el-table-column>
            </el-table>
          </div>
        </el-card>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {
  ref,
  reactive,
  onMounted,
  nextTick,
  onUnmounted,
  computed,
  watch,
} from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import { useECharts } from "@pureadmin/utils";
import type { EChartsOption } from "echarts";
import type { Ref } from "vue";
import { getAssetProfileGraph } from "../servers";

const route = useRoute();
const router = useRouter();

// Props
const props = defineProps<{
  task?: string;
}>();

// 页面状态
const loading = ref(false);
const searchKeyword = ref("");
const graphChartRef = ref<HTMLDivElement>();
let chartInstance: any = null;

// 资产信息
const assetInfo = reactive({
  asset_id: 0,
  ip: "",
  port: "",
  service: "",
  task_name: "",
  current_department: "",
  is_high_risk: false,
  is_internet_accessible: false,
});

// 图谱数据
const graphData = ref<{
  nodes: any[];
  links: any[];
}>({
  nodes: [],
  links: [],
});

// 面板数据
const departmentCandidates = ref<any[]>([]);
const keywords = ref<any[]>([]);
const similarAssets = ref<any[]>([]);

// ECharts 图表实例
const { setOptions, getInstance, resize } = useECharts(
  graphChartRef as Ref<HTMLDivElement>
);

// 获取节点样式
const getNodeStyle = (nodeType: string, data?: any) => {
  const styles: any = {
    asset: {
      shape: "rect",
      size: [140, 70],
      color: "#1890ff",
      borderColor: "#fff",
      borderWidth: 3,
      fontColor: "#fff",
      fontSize: 14,
    },
    department: {
      shape: "circle",
      size: 60,
      color: "#52c41a",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#fff",
      fontSize: 12,
    },
    department_candidate: {
      shape: "circle",
      size: 50,
      color: "#faad14",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#333",
      fontSize: 11,
    },
    task: {
      shape: "diamond",
      size: 50,
      color: "#722ed1",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#fff",
      fontSize: 11,
    },
    ip_range: {
      shape: "diamond",
      size: 50,
      color: "#722ed1",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#fff",
      fontSize: 11,
    },
    keyword: {
      shape: "triangle",
      size: 40,
      color: "#13c2c2",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#333",
      fontSize: 10,
    },
    similar_asset: {
      shape: "triangle",
      size: 40,
      color: "#13c2c2",
      borderColor: "#fff",
      borderWidth: 2,
      fontColor: "#333",
      fontSize: 10,
    },
  };

  return styles[nodeType] || styles.asset;
};

// 获取边样式
const getEdgeStyle = (edgeType: string) => {
  const styles: any = {
    belongs_to_task: {
      color: "#722ed1",
      width: 2,
      style: "solid",
    },
    in_ip_range: {
      color: "#722ed1",
      width: 2,
      style: "dashed",
    },
    recommended_department: {
      color: "#faad14",
      width: 3,
      style: "solid",
    },
    current_department: {
      color: "#52c41a",
      width: 3,
      style: "solid",
    },
    hit_keyword: {
      color: "#13c2c2",
      width: 2,
      style: "dashed",
    },
    similar_to: {
      color: "#13c2c2",
      width: 2,
      style: "dashed",
    },
  };

  return styles[edgeType] || styles.belongs_to_task;
};

// 转换API数据为ECharts Graph格式
const transformGraphData = (apiData: any) => {
  if (!apiData || !apiData.graph) {
    return { nodes: [], links: [] };
  }

  const nodes = Array.isArray(apiData.graph.nodes) ? apiData.graph.nodes : [];
  const edges = Array.isArray(apiData.graph.edges) ? apiData.graph.edges : [];

  // 转换节点为ECharts格式
  const echartsNodes = nodes.map((node: any) => {
    const style = getNodeStyle(node.type, node.data);
    const size = Array.isArray(style.size) ? style.size[0] : style.size;
    const height = Array.isArray(style.size) ? style.size[1] : size;

    return {
      id: node.id,
      name: node.label || node.id,
      symbolSize: Array.isArray(style.size) ? style.size : [size, height],
      symbol:
        style.shape === "circle"
          ? "circle"
          : style.shape === "diamond"
          ? "diamond"
          : style.shape === "triangle"
          ? "triangle"
          : "rect",
      itemStyle: {
        color: style.color,
        borderColor: style.borderColor,
        borderWidth: style.borderWidth,
      },
      label: {
        show: true,
        position: "inside",
        color: style.fontColor,
        fontSize: style.fontSize,
        formatter: (params: any) => {
          const name = params.name || "";
          // 如果名称太长，换行显示
          if (name.length > 12) {
            return name.substring(0, 12) + "\n" + name.substring(12);
          }
          return name;
        },
      },
      category: node.type,
      nodeType: node.type,
      data: node.data || {},
    };
  });

  // 转换边为ECharts格式
  const echartsLinks = edges.map((edge: any) => {
    const style = getEdgeStyle(edge.type);

    return {
      source: edge.source,
      target: edge.target,
      lineStyle: {
        color: style.color,
        width: style.width,
        type: style.style === "dashed" ? "dashed" : "solid",
        curveness: 0.3,
      },
      edgeType: edge.type,
      label: {
        show: edge.label ? true : false,
        formatter: edge.label || "",
        fontSize: 10,
      },
      data: edge.data || {},
    };
  });

  return { nodes: echartsNodes, links: echartsLinks };
};

// 渲染ECharts图谱
const renderGraph = (nodes: any[], links: any[]) => {
  const option: EChartsOption = {
    tooltip: {
      show: true,
      formatter: (params: any) => {
        if (params.dataType === "node") {
          const node = params.data;
          let content = `<div style="font-weight: bold; margin-bottom: 5px;">${node.name}</div>`;
          if (node.nodeType === "asset") {
            content += `<div>IP: ${node.data?.ip || "未知"}</div>`;
            content += `<div>端口: ${node.data?.port || "未知"}</div>`;
          } else if (
            node.nodeType === "department" ||
            node.nodeType === "department_candidate"
          ) {
            content += `<div>置信度: ${
              node.data?.confidence
                ? (node.data.confidence * 100).toFixed(0) + "%"
                : "未知"
            }</div>`;
          }
          return content;
        }
        if (params.dataType === "edge") {
          const edge = params.data;
          return edge.label?.formatter || edge.edgeType;
        }
        return "";
      },
    },
    series: [
      {
        type: "graph",
        layout: "force",
        data: nodes,
        links: links,
        categories: [
          { name: "asset", itemStyle: { color: "#1890ff" } },
          { name: "department", itemStyle: { color: "#52c41a" } },
          { name: "department_candidate", itemStyle: { color: "#faad14" } },
          { name: "task", itemStyle: { color: "#722ed1" } },
          { name: "ip_range", itemStyle: { color: "#722ed1" } },
          { name: "keyword", itemStyle: { color: "#13c2c2" } },
          { name: "similar_asset", itemStyle: { color: "#13c2c2" } },
        ],
        roam: true,
        label: {
          show: true,
          position: "inside",
          fontSize: 12,
        },
        labelLayout: {
          hideOverlap: false,
        },
        emphasis: {
          focus: "adjacency",
          lineStyle: {
            width: 4,
          },
        },
        lineStyle: {
          curveness: 0.3,
        },
        force: {
          repulsion: 2000,
          gravity: 0.1,
          edgeLength: 150,
          layoutAnimation: true,
        },
      },
    ],
  };

  setOptions(option);
  chartInstance = getInstance();
};

// 加载图谱数据
const loadGraphData = async () => {
  // 从路由参数获取asset_id
  let assetId: number | null = null;

  // 优先从params获取，如果没有则从query获取
  if (route.params.asset_id) {
    assetId = Number(route.params.asset_id);
  } else if (route.query.asset_id) {
    assetId = Number(route.query.asset_id);
  }

  if (!assetId || isNaN(assetId)) {
    ElMessage.error("缺少资产ID参数");
    return;
  }

  try {
    loading.value = true;

    const res = await getAssetProfileGraph({
      asset_id: assetId,
      with_debug: false,
    });

    if (res.code === 2000 && res.data) {
      // 更新资产信息
      if (res.data.asset) {
        Object.assign(assetInfo, {
          asset_id: res.data.asset.asset_id || assetId,
          ip: res.data.asset.ip || "",
          port: res.data.asset.port || "",
          service: res.data.asset.service || "",
          task_name: res.data.asset.task_name || "",
          current_department: res.data.asset.current_department || "",
          is_high_risk: res.data.asset.is_high_risk || false,
          is_internet_accessible:
            res.data.asset.is_internet_accessible || false,
        });
      }

      // 更新图谱数据
      const { nodes, links } = transformGraphData(res.data);
      graphData.value = { nodes, links };

      // 更新面板数据
      if (res.data.panels) {
        departmentCandidates.value =
          res.data.panels.department_candidates || [];
        keywords.value = res.data.panels.keywords || [];
        similarAssets.value = res.data.panels.similar_assets || [];
      }

      // 渲染图谱
      await nextTick();
      if (nodes.length > 0) {
        renderGraph(nodes, links);
      } else {
        ElMessage.warning("暂无图谱数据");
      }
    }
  } catch (error: any) {
    ElMessage.error(error?.message || "获取资产画像图谱失败");
  } finally {
    loading.value = false;
  }
};

// 搜索节点
const searchNodes = () => {
  if (!searchKeyword.value.trim()) {
    ElMessage.warning("请输入搜索关键词");
    return;
  }

  if (chartInstance) {
    const option = chartInstance.getOption();
    const nodes = option.series[0].data;
    const foundIndex = nodes.findIndex((n: any) =>
      n.name.includes(searchKeyword.value)
    );

    if (foundIndex >= 0) {
      chartInstance.dispatchAction({
        type: "highlight",
        dataIndex: foundIndex,
      });

      setTimeout(() => {
        if (chartInstance) {
          chartInstance.dispatchAction({
            type: "downplay",
            dataIndex: foundIndex,
          });
        }
      }, 3000);
    } else {
      ElMessage.info("未找到匹配的节点");
    }
  }
};

// 刷新图谱
const refreshGraph = () => {
  loadGraphData();
};

// 导出图谱PNG
const exportGraphPNG = () => {
  if (chartInstance) {
    const url = chartInstance.getDataURL({
      type: "png",
      pixelRatio: 2,
      backgroundColor: "#fff",
    });
    const link = document.createElement("a");
    link.href = url;
    link.download = `asset_profile_graph_${new Date().getTime()}.png`;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    ElMessage.success("图谱导出成功");
  }
};

// 返回
const handleBack = () => {
  router.back();
};

// 监听路由参数变化，自动加载数据
watch(
  () => [route.params.asset_id, route.query.asset_id],
  () => {
    if (route.params.asset_id || route.query.asset_id) {
      loadGraphData();
    }
  },
  { immediate: true }
);

// 初始化
onMounted(async () => {
  await nextTick();
  // 如果路由中有asset_id，加载数据
  if (route.params.asset_id || route.query.asset_id) {
    await loadGraphData();
  }
});

// 组件卸载时清理
onUnmounted(() => {
  if (chartInstance) {
    chartInstance.dispose();
  }
});

// 暴露 resizeChart 方法供父组件调用
const resizeChart = () => {
  if (chartInstance) {
    chartInstance.resize();
  }
};

defineExpose({
  resizeChart,
});
</script>

<style scoped lang="scss">
.asset-profile-graph-page {
  padding: 20px;
  background-color: #f5f7fa;
  min-height: 100vh;
  box-sizing: border-box;
}

.page-header {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 20px;

  .page-title {
    font-size: 18px;
    font-weight: 600;
    margin: 0;
    color: #333;
  }
}

.graph-content {
  display: flex;
  gap: 20px;
  height: calc(100vh - 120px);
}

.left-panel {
  width: 300px;
  display: flex;
  flex-direction: column;
  gap: 15px;
  overflow-y: auto;
}

.info-card,
.tags-card {
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

  .card-title {
    font-size: 14px;
    font-weight: 500;
    margin: 0 0 12px 0;
    color: #333;
  }
}

.tags-container {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.no-dept {
  color: #909399;
  font-size: 12px;
}

.graph-area {
  flex: 1;
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.graph-toolbar {
  padding: 10px 16px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;

  .toolbar-left {
    flex: 1;
  }

  .search-input {
    width: 300px;
  }
}

.graph-container {
  flex: 1;
  position: relative;
  overflow: hidden;
}

.echarts-graph {
  width: 100%;
  height: 100%;
  min-height: 600px;
}

.right-panel {
  width: 350px;
  display: flex;
  flex-direction: column;
  gap: 15px;
  overflow-y: auto;
}

.legend-card,
.detail-card {
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);

  .card-title {
    font-size: 14px;
    font-weight: 500;
    margin: 0 0 12px 0;
    color: #333;
  }
}

.legend-list {
  list-style: none;
  padding: 0;
  margin: 0;

  li {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 13px;
    color: #666;
  }
}

.legend-icon {
  display: inline-block;
  width: 16px;
  height: 16px;

  &.asset {
    background-color: #1890ff;
    border-radius: 2px;
  }

  &.department {
    background-color: #52c41a;
    border-radius: 50%;
  }

  &.task {
    background-color: #722ed1;
    transform: rotate(45deg);
    border-radius: 2px;
  }

  &.keyword {
    background-color: #13c2c2;
    clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  }
}

.detail-section {
  margin-top: 16px;

  h4 {
    font-size: 13px;
    color: #333;
    margin-bottom: 8px;
    font-weight: 500;
  }
}

@media (max-width: 1600px) {
  .left-panel {
    width: 260px;
  }

  .right-panel {
    width: 300px;
  }

  .search-input {
    width: 200px;
  }
}
</style>
